home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Meeting Pearls 4
/
Meeting Pearls Vol. IV (1996)(GTI - Schatztruhe)[!].iso
/
Pearls
/
dev
/
C-Lib
/
AsyncIO
/
source
/
SeekAsync.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-12-29
|
8KB
|
232 lines
#include "async.h"
_ASM LibCall _ARGS LONG
SeekAsync( _REG( a0 ) AsyncFile *file, _REG( d0 ) LONG position, _REG( d1 ) SeekModes mode )
{
#ifdef ASIO_NOEXTERNALS
struct DosLibrary *DOSBase = file->af_DOSBase;
#endif
LONG current, target;
LONG minBuf, maxBuf;
LONG bytesArrived;
LONG diff;
LONG filePos;
LONG roundTarget;
D_S( struct FileInfoBlock, fib );
bytesArrived = AS_WaitPacket( file );
if( bytesArrived < 0 )
{
return( -1 );
}
if( file->af_ReadMode )
{
/* figure out what the actual file position is */
filePos = Seek( file->af_File, 0, OFFSET_CURRENT );
if( filePos < 0 )
{
AS_RecordSyncFailure( file );
return( -1 );
}
/* figure out what the caller's file position is */
current = filePos - ( file->af_BytesLeft + bytesArrived ) + file->af_SeekOffset;
/* We can't clear af_SeekOffset here. If another seek is done directly
* after this one, it would mean that we will both return and start
* reading from the wrong position.
*/
/* file->af_SeekOffset = 0; */
/* figure out the absolute offset within the file where we must seek to */
if( mode == MODE_CURRENT )
{
target = current + position;
}
else if( mode == MODE_START )
{
target = position;
}
else /* if( mode == MODE_END ) */
{
if( !ExamineFH( file->af_File, fib ) )
{
AS_RecordSyncFailure( file );
return( -1 );
}
target = fib->fib_Size + position;
}
/* MH: Here we must be able to handle two different situations:
* 1) A seek directly after having dropped both buffers, and started
* refilling (typical case: File open).
* 2) Other seeks (typical case: A seek after some initial reading).
*
* We need to subtract with "af_Buffers[ 1 - file->af_CurrentBuf ]",
* as af_CurrentBuf refers to the *arrived* buffer, not the one we're
* currently reading from (and af_Offset points into the buffer we're
* reading from)!
*
* In case 1, there will be only one packet received. af_CurrentBuf
* will be zero, and refers to the newly arrived buffer (as it
* should). For proper behaviour in the minBuf calculation, we have
* set af_Offset to point to af_Buffers[ 1 ], when starting reading
* to empty buffers. That way wee need no special case code here.
* ReadAsync() can handle this, as af_BytesLeft == 0 in that case.
*/
/* figure out what range of the file is currently in our buffers */
minBuf = current - ( LONG ) ( file->af_Offset - file->af_Buffers[ 1 - file->af_CurrentBuf ] );
maxBuf = current + file->af_BytesLeft + bytesArrived; /* WARNING: this is one too big */
diff = target - current;
if( ( target < minBuf ) || ( target >= maxBuf ) )
{
/* the target seek location isn't currently in our buffers, so
* move the actual file pointer to the desired location, and then
* restart the async read thing...
*/
/* this is to keep our file reading block-aligned on the device.
* block-aligned reads are generally quite a bit faster, so it is
* worth the trouble to keep things aligned
*/
roundTarget = ( target / file->af_BlockSize ) * file->af_BlockSize;
if( Seek( file->af_File, roundTarget - filePos, OFFSET_CURRENT ) < 0 )
{
AS_RecordSyncFailure( file );
return( -1 );
}
AS_SendPacket( file, file->af_Buffers[ 0 ] );
file->af_SeekOffset = target - roundTarget;
file->af_BytesLeft = 0;
file->af_CurrentBuf = 0;
/* MH: We set af_Offset to the buffer not being filled, to be able to
* handle a new seek directly after this one (see above; minBuf
* calculation). If we start reading after this seek, ReadAsync()
* will handle everything correctly, as af_BytesLeft == 0.
*/
file->af_Offset = file->af_Buffers[ 1 ];
}
else if( ( target < current ) || ( diff <= file->af_BytesLeft ) )
{
/* one of the two following things is true:
*
* 1. The target seek location is within the current read buffer,
* but before the current location within the buffer. Move back
* within the buffer and pretend we never got the pending packet,
* just to make life easier, and faster, in the read routine.
*
* 2. The target seek location is ahead within the current
* read buffer. Advance to that location. As above, pretend to
* have never received the pending packet.
*/
AS_RequeuePacket( file );
file->af_BytesLeft -= diff;
file->af_Offset += diff;
/* MH: We don't need to clear the seek offset here, since
* if we get here, we must have read some data from the current
* buffer, and af_SeekOffset will be zero then (done by
* ReadAsync()).
*/
}
else
{
/* at this point, we know the target seek location is within
* the buffer filled in by the packet that we just received
* at the start of this function. Throw away all the bytes in the
* current buffer, send a packet out to get the async thing going
* again, readjust buffer pointers to the seek location, and return
* with a grin on your face... :-)
*/
/* MH: Don't read to the buffer we just got, but the other one! */
AS_SendPacket( file, file->af_Buffers[ 1 - file->af_CurrentBuf ] );
/* MH: Account for bytes left in buffer we drop *and* the af_SeekOffset.
*/
diff -= file->af_BytesLeft - file->af_SeekOffset;
/* MH: Set the offset into the current (newly arrived) buffer */
file->af_Offset = file->af_Buffers[ file->af_CurrentBuf ] + diff;
file->af_BytesLeft = bytesArrived - diff;
/* MH: We need to clear the seek offset here, since we can't do it above.
*/
file->af_SeekOffset = 0;
/* MH: This "buffer switching" is important to do. It wasn't done!
* This explains the errors one could encounter now and then.
* The AS_SendPacket() call above is not the cause, and *is* correct.
*/
file->af_CurrentBuf = 1 - file->af_CurrentBuf;
}
}
else
{
/* flush the buffers */
if( file->af_BufferSize > file->af_BytesLeft )
{
if( Write(
file->af_File,
file->af_Buffers[ file->af_CurrentBuf ],
file->af_BufferSize - file->af_BytesLeft ) < 0 )
{
AS_RecordSyncFailure( file );
return( -1 );
}
}
/* this will unfortunately generally result in non block-aligned file
* access. We could be sneaky and try to resync our file pos at a
* later time, but we won't bother. Seeking in write-only files is
* relatively rare (except when writing IFF files with unknown chunk
* sizes, where the chunk size has to be written after the chunk data)
*/
/* MH: Ideas on how to improve the above (not tested, since I don't need
* the SeekAsync for writing in any of my programs at the moment! ;):
*
* Add a new field to the AsyncFile struct. af_WriteOffset or something like
* that (af_SeekOffset can probably be used). Like in the read case, we
* calculate a roundTarget, but we don't seek to that (but rather to the
* "absolute" position), and save the difference in the struct. af_BytesLeft
* and af_Offset are adjusted to point into the "middle" of the buffer,
* where the write will occur. Writes then needs some minor changes:
* Instead of simply writing the buffer from the start, we add the offset
* (saved above) to the buffer base, and write the partial buffer. The
* offset is then cleared. Voila: The file still block-aligned, at the price
* of some non-optimal buffer usage.
*
* Problem: As it is now, Arg3 in the packet is always set to the buffer size.
* With the above fix, this would have to be updated for each SendPacket (i.e.
* a new argument would be needed).
*/
current = Seek( file->af_File, position, mode );
if( current < 0 )
{
AS_RecordSyncFailure( file );
return( -1 );
}
file->af_BytesLeft = file->af_BufferSize;
file->af_CurrentBuf = 0;
file->af_Offset = file->af_Buffers[ 0 ];
}
return( current );
}